#include "test.h"

#include <chrono>
#include <cstring>
#include <functional>
#include <sstream>

#include <include/core/SkBitmap.h>
#include <include/core/SkCanvas.h>
#include <include/core/SkFontMgr.h>
#include <include/core/SkPath.h>
#include <include/core/SkTextBlob.h>

using namespace std::chrono_literals;

Test::Test(int winWidth, int winHeight)
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
    constexpr auto centered = SDL_WINDOWPOS_CENTERED;
    constexpr auto flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN;
    window_ = SDL_CreateWindow("libtxt Test", centered, centered, winWidth, winHeight, flags);
    renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED);
    SDL_RenderClear(renderer_);
    rect_ = { 0, 0, winWidth, winHeight };

    typeface_ = SkTypeface::MakeFromFile("out/DroidSansFallback.ttf");

    uiThread_ = std::make_unique<std::thread>(std::bind(&Test::UIThreadTest, this));
}

Test::~Test()
{
    uiThread_->join();
    if (surface_) {
        SDL_FreeSurface(surface_);
    }

    if (texture_) {
        SDL_DestroyTexture(texture_);
    }

    if (renderer_) {
        SDL_DestroyRenderer(renderer_);
    }

    if (window_) {
        SDL_DestroyWindow(window_);
    }

    SDL_Quit();
}

void Test::Run()
{
    while (uiThreadRunning_) {
        SDL_Event e = {};
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                uiThreadRunning_ = false;
            }

            if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                SDL_GetWindowSize(window_, &rect_.w, &rect_.h);
            }

            if (e.type == SDL_MOUSEMOTION && e.motion.type == SDL_MOUSEMOTION) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                x_ = e.motion.x;
                y_ = e.motion.y;
            }

            if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                left_ = false;
            }

            if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                left_ = true;
            }

            if (e.type == SDL_MOUSEWHEEL) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                scale_ = scale_ * (1 + 0.2 * e.wheel.y);
            }

            if (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_ESCAPE) {
                std::lock_guard<std::mutex> lock(propsMutex_);
                exit(0);
            }
        }
    }
}

void Test::UIThreadTest()
{
    while (uiThreadRunning_) {
        if (surface_) {
            SDL_FreeSurface(surface_);
            surface_ = nullptr;
        }

        if (texture_) {
            SDL_DestroyTexture(texture_);
            texture_ = nullptr;
        }

        decltype(rect_) rect;
        SkBitmap bitmap;
        {
            std::lock_guard<std::mutex> lock(propsMutex_);
            rect = rect_;
            bitmap.setInfo(SkImageInfo::Make(rect_.w, rect_.h,
                                             kBGRA_8888_SkColorType,
                                             kOpaque_SkAlphaType));
            bitmap.allocPixels();
            SkCanvas canvas(bitmap);
            canvas.clear(SK_ColorWHITE);
            canvas.setMatrix(SkMatrix::Scale(scale_, scale_));
            Draw(canvas);
            DrawAfter(canvas);
        }
        constexpr uint32_t rmask = 0x00ff0000;
        constexpr uint32_t gmask = 0x0000ff00;
        constexpr uint32_t bmask = 0x000000ff;
        constexpr uint32_t amask = 0xff000000;
        surface_ = SDL_CreateRGBSurfaceFrom(bitmap.getPixels(),
                                            rect.w, rect.h,
                                            32, rect.w * 4,
                                            rmask, gmask,
                                            bmask, amask);
        texture_ = SDL_CreateTextureFromSurface(renderer_, surface_);
        SDL_RenderCopy(renderer_, texture_, NULL, &rect);
        SDL_RenderPresent(renderer_);
        std::this_thread::sleep_for(16ms);
    }
}

void Test::Draw(SkCanvas &canvas)
{
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setColor(0x33000000);
    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(1);

    SkPaint textPaint;
    textPaint.setAntiAlias(true);
    textPaint.setColor(0xff00007f);

    SkFont font;
    font.setTypeface(typeface_);
    font.setSize(16);

    SkPath path;
    for (int i = 0; i < rect_.h / scale_ / 100; i++) {
        path.moveTo(0, i * 100);
        path.lineTo(1e9, i * 100);

        std::stringstream ss;
        ss << i * 100;
        canvas.drawString(ss.str().c_str(),
                          0,
                          font.getSize() + i * 100,
                          font, textPaint);
    }

    for (int i = 0; i < rect_.w / scale_ / 100; i++) {
        path.moveTo(i * 100, 0);
        path.lineTo(i * 100, 1e9);

        std::stringstream ss;
        ss << i * 100;
        canvas.drawString(ss.str().c_str(),
                          i * 100,
                          font.getSize() + 0,
                          font, textPaint);
    }

    canvas.drawPath(path, paint);
}

void Test::DrawAfter(SkCanvas &canvas)
{
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setColor(0x33000000);
    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(1);

    SkPaint textPaint;
    textPaint.setAntiAlias(true);
    textPaint.setColor(0xff0000ff);
    textPaint.setStyle(SkPaint::kStroke_Style);
    textPaint.setStrokeWidth(1);

    SkFont font;
    font.setTypeface(typeface_);
    font.setSize(16);

    SkPath path;
    if (left_) {
        path.moveTo(x_ / scale_, 0);
        path.lineTo(x_ / scale_, 1e9);
        path.moveTo(0, y_ / scale_);
        path.lineTo(1e9, y_ / scale_);

        std::stringstream ss;
        ss << "(" << x_ / scale_ << ", " << y_ / scale_ << ")";
        canvas.drawString(ss.str().c_str(),
                          x_ / scale_ + 10,
                          font.getSize() + y_ / scale_ + 10,
                          font, textPaint);
    }

    canvas.drawPath(path, paint);
}
